﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Policy;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Hosting;
using System.Web.Http.Routing;
using System.Web.Http.Routing;
using AutoMapper;
using EmployeesManager;
using EmployeesManager.Controllers;
using EmployeesManager.Models;
using EmployeesManager.Poco;
using EmployeesManagerDataAccessContracts;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace ManagerUnitTests
{
    [TestClass]
    public class EmployeesControllerTest
    {
        [TestInitialize]
        public void AfterAssemblyInitializeEventArgs()
        {
            Mapper.CreateMap<Employee, EmployeeForm>();
            Mapper.CreateMap<EmployeeForm, Employee>();
        }

        #region Get

        [TestMethod]
        public void GetEmployees_Action_Returns_Valid_IEnumerable_Of_Employees()
        {
            // Arrange
            var mockRepo = new Mock<IRepository<Employee>>();
            var expectedRet = TestHelpers.ValidEmployeesList();
            mockRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(mockRepo);

            // Act
            var result = employeesController.GetEmployees();

            // Assert
            Assert.IsInstanceOfType(result, typeof (IEnumerable<EmployeeForm>),
                                    "The employeesController.GetEmployees() return is not of type IEnumerable<Employee>");
            Assert.IsNotNull(result, "The employeesController.GetEmployees() return is null");
            var res = result.ToList();
            Assert.AreEqual(expectedRet.Count(), res.Count(),
                            string.Format(
                                "The employeesController.GetEmployees() return count is {0} it's supposed to be {1} (not correct)",
                                res.Count(), expectedRet.Count()));
        }

        #endregion

        #region Post

        [TestMethod]
        public void The_PostEmployee_Action_returns_created_statuscode_When_The_Employee_Model_Is_Valid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var employee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PostEmployee(employee), employee);

            // Assert
            Assert.IsNotNull(result, "Should have returned a HttpResponseMessage");
            Assert.IsTrue(result.IsSuccessStatusCode, "Status Code Should be success");
            Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
        }

        [TestMethod]
        public void The_PostEmployee_Action_Calls_Save_When_The_Employee_Model_Is_Valid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var employee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);

            employeesRepo.Setup(m => m.Save(It.IsAny<Employee>()));

            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PostEmployee(employee), employee);

            // Assert
            employeesRepo.Verify(x => x.Save(It.IsAny<Employee>()), Times.Once(),
                                 "EmployeesController should have called save on repository");
        }

        [TestMethod]
        public void The_PostEmployee_Action_returns_bad_Request_When_The_Employee_Salary_Is_Invalid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var invalidEmployee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);
            invalidEmployee.Salary = -1;

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PostEmployee(invalidEmployee),
                                                                     invalidEmployee);

            // Assert
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be fail");
            Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode,
                            "status code should be BadRequest in case of invalid employee");
        }

        [TestMethod]
        public void The_PostEmployee_Action_returns_bad_Request_When_The_Employee_Age_Is_Invalid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var invalidEmployee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);
            invalidEmployee.Age = -1;

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PostEmployee(invalidEmployee),
                                                                     invalidEmployee);

            // Assert
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be fail");
            Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode,
                            "status code should be BadRequest in case of invalid employee");
        }

        [TestMethod]
        public void The_PostEmployee_Action_returns_bad_Request_When_The_Employee_Name_Is_Empty()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var invalidEmployee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);
            invalidEmployee.Name = "";

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PostEmployee(invalidEmployee),
                                                                     invalidEmployee);

            // Assert
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be fail");
            Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode,
                            "status code should be BadRequest in case of invalid employee");
        }

        #endregion

        #region Delete

        [TestMethod]
        public void DeleteEmployee_Calls_Repository_Delete()
        {
            var employeesRepo = new Mock<IRepository<Employee>>();
            int itemToDelete = 3;
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            employeesRepo.Setup(x => x.Delete(itemToDelete));
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);

            employeesController.DeleteEmployee(itemToDelete);

            employeesRepo.Verify(x => x.Delete(itemToDelete), Times.Once(),
                                 "EmployeesController should have called Delete on repository");
        }

        [TestMethod]
        public void DeleteEmployee_Returns_Response_Message_With_NotFound_StatusCode()
        {
            var employeesRepo = new Mock<IRepository<Employee>>();
            int itemToDelete = 355;
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);

            var result = employeesController.DeleteEmployee(itemToDelete);

            Assert.IsInstanceOfType(result, typeof (HttpResponseMessage), "Should return HttpResponseMessage");
            Assert.AreEqual(HttpStatusCode.NotFound, result.StatusCode, "Should return StatusCode == NotFound");
        }

        [TestMethod]
        public void DeleteEmployee_Returns_Response_Message_With_Accepted_StatusCode()
        {
            var employeesRepo = new Mock<IRepository<Employee>>();
            int itemToDelete = 3;
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);

            var result = employeesController.DeleteEmployee(itemToDelete);

            Assert.IsInstanceOfType(result, typeof (HttpResponseMessage), "Should return HttpResponseMessage");
            Assert.AreEqual(HttpStatusCode.Accepted, result.StatusCode, "Should return StatusCode == Accepted");
        }

        #endregion

        #region Put

        [TestMethod]
        public void The_PutEmployee_Action_returns_Ok_statuscode_When_The_Employee_Exists()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var employee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[2]);

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PutEmployee(3,employee), employee);

            // Assert
            Assert.IsNotNull(result, "Should have returned a HttpResponseMessage");
            Assert.IsTrue(result.IsSuccessStatusCode, "Status Code Should be success");
            Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
        }

        [TestMethod]
        public void The_PutEmployee_Action_returns_NotFound_statuscode_When_The_Employee_Does_NOT_Exists()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var employee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[2]);
            int notExistId = 876;
            // Act
            var result = employeesController.CallWithModelValidation(m => m.PutEmployee(notExistId,employee), employee);

            // Assert
            Assert.IsNotNull(result, "Should have returned a HttpResponseMessage");
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be success");
            Assert.AreEqual(HttpStatusCode.NotFound, result.StatusCode, "Invalid status code");
        }

        [TestMethod]
        public void The_PutEmployee_Action_Calls_Save_When_The_Employee_Model_Is_Valid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[2]);

            employeesRepo.Setup(m => m.Save(It.IsAny<Employee>()));

            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PutEmployee(3, employee), employee);

            // Assert
            employeesRepo.Verify(x => x.Save(It.IsAny<Employee>()), Times.Once(),
                                 "EmployeesController should have called save on repository");
        }

        [TestMethod]
        public void The_PutEmployee_Action_returns_bad_Request_When_The_Employee_Salary_Is_Invalid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var invalidEmployee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);
            invalidEmployee.Salary = -1;

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PutEmployee(1,invalidEmployee),
                                                                     invalidEmployee);

            // Assert
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be fail");
            Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode,
                            "status code should be BadRequest in case of invalid employee");
        }

        [TestMethod]
        public void The_PutEmployee_Action_returns_bad_Request_When_The_Employee_Age_Is_Invalid()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var invalidEmployee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);
            invalidEmployee.Age = -1;

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PutEmployee(1,invalidEmployee),
                                                                     invalidEmployee);

            // Assert
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be fail");
            Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode,
                            "status code should be BadRequest in case of invalid employee");
        }

        [TestMethod]
        public void The_PutEmployee_Action_returns_bad_Request_When_The_Employee_Name_Is_Empty()
        {
            // Arrange
            var employeesRepo = new Mock<IRepository<Employee>>();
            var expectedRet = TestHelpers.ValidEmployeesList();
            employeesRepo.Setup(x => x.Entities).Returns(expectedRet);
            var employeesController = TestHelpers.SetupControllerForTests(employeesRepo);
            var invalidEmployee = Mapper.Map<Employee, EmployeeForm>(TestHelpers.ValidEmployeesList().ToList()[0]);
            invalidEmployee.Name = "";

            // Act
            var result = employeesController.CallWithModelValidation(m => m.PutEmployee(1,invalidEmployee),
                                                                     invalidEmployee);

            // Assert
            Assert.IsFalse(result.IsSuccessStatusCode, "Status Code Should be fail");
            Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode,
                            "status code should be BadRequest in case of invalid employee");
        }

        #endregion

    }
}
